﻿/************************************************************************
* Copyright (c) 2018 All Rights Reserved.
*命名空间：Wx.Sdk.Api.Src
*文件名： Message
*创建人： kingling
*创建时间：2018/9/29 13:26:34
*描述
*=======================================================================
*修改标记
*修改时间：2018/9/29 13:26:34
*修改人：kingling
*描述：
************************************************************************/

using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Wx.Sdk.Api.Enums;
using Wx.Sdk.Api.Entity.Messages;
using Wx.Sdk.Api.Entity.Base;
using Wx.Sdk.Api.Entity.Replys;
using Wx.Sdk.Utils;
using System.Linq;
using Wx.Sdk.Api.Entity;

namespace Wx.Sdk.Api.Src
{
    public abstract class WxMessage : IWxMessage
    {
        protected WxApi wxApi;
        HttpContext context;
        string reply;
        protected string openId;
        protected string mchId;
        public WxMessage(WxApi wxApi)
        {
            this.wxApi = wxApi;
            context = this.wxApi.httpContextAccessor.HttpContext;
        }
        #region 消息
        /// <summary>
        /// 只要消息进来,就执行
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void Msg(Msg msg);
        /// <summary>
        /// 文字消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void TextMsg(TextMsg msg);
        /// <summary>
        /// 图片消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void ImageMsg(ImageMsg msg);
        /// <summary>
        /// 语音消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void VoiceMsg(VoiceMsg msg);
        /// <summary>
        /// 视频、短视频消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void VideoMsg(VideoMsg msg);
        /// <summary>
        /// 地理位置消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void LocationMsg(LocationMsg msg);
        /// <summary>
        /// 链接消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected abstract void LinkMsg(LinkMsg msg);
        #endregion

        #region 事件
        /// <summary>
        /// 上报地理位置事件
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void LocationEvent(LocationEvent evt);
        /// <summary>
        /// 扫描带参数二维码事件:1. 用户未关注时，进行关注后的事件推送,事件类型，subscribe.2. 用户已关注时的事件推送,事件类型，SCAN
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void ScanQRCodeEvent(ScanQRCodeEvent evt);
        /// <summary>
        /// 取消关注事件
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void UnSubscribeEvent(UnSubscribeEvent evt);
        /// <summary>
        /// 关注事件
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void SubscribeEvent(SubscribeEvent evt);
        /// <summary>
        /// 菜单点击事件
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void MenuClickEvent(MenuEvent evt);
        /// <summary>
        /// 菜单的Url点击事件
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void MenuViewEvent(MenuEvent evt);
        /// <summary>
        /// 模板消息发送结束
        /// </summary>
        /// <param name="evt"></param>
        protected abstract void TemplateMsgEvent(TemplateMsgEvent evt);
        #endregion

        #region 获取用户信息
        /// <summary>
        /// 获取用户信喜
        /// </summary>
        /// <returns></returns>
        protected async Task<WxUser> GetWxUser() {
            var access_token = wxApi.apiBase.GetAccessToken();
            var url = $"https://api.weixin.qq.com/cgi-bin/user/info?access_token={access_token}&openid={openId}&lang=zh_CN";
            var str =await wxApi.apiBase.Get(url);
            var user = JsonHelper.ToModel<WxUser>(str);
            return user;
        }
        #endregion

        #region 被动回复
        /// <summary>
        /// 被动回复文字消息
        /// </summary>
        /// <param name="content"></param>
        protected void ReplyTextMsg(string content)
        {
            var replyMsg = new ReplyTextMsg
            {
                FromUserName = mchId,
                ToUserName = openId,
                Content = content
            };
            reply = XmlHelper.ToXml(replyMsg);
        }
        /// <summary>
        /// 回复图文消息 最大8条
        /// </summary>
        /// <param name="articles"></param>
        protected void ReplyNewsMsg(params Article[] articles)
        {
            var replyMsg = new ReplyNewsMsg
            {
                FromUserName = mchId,
                ToUserName = openId,
                ArticleCount = articles.Length,
                Articles = articles.Take(8).ToList()
            };
            reply = XmlHelper.ToXml(replyMsg);
        }
        /// <summary>
        /// 回复图片消息
        /// </summary>
        /// <param name="mediaId">上传图片的媒体ID</param>
        protected void ReplyImageMsg(string mediaId)
        {
            var replyMsg = new ReplyImageMsg
            {
                FromUserName = mchId,
                ToUserName = openId,
                Image = new Image
                {
                    MediaId = mediaId
                }
            };
            reply = XmlHelper.ToXml(replyMsg);
        }
        /// <summary>
        /// 回复音乐消息
        /// </summary>
        /// <param name="music"></param>
        protected void ReplyMusicMsg(Music music)
        {
            var replyMsg = new ReplyMusicMsg
            {
                FromUserName = mchId,
                ToUserName = openId,
                Music = music
            };
            reply = XmlHelper.ToXml(replyMsg);
        }
        /// <summary>
        /// 回复视频消息
        /// </summary>
        /// <param name="mediaId">上传的素材ID</param>
        /// <param name="title">视频标题</param>
        /// <param name="des">视频描述</param>
        protected void ReplyVideoMsg(string mediaId, string title, string des)
        {
            var replyMsg = new ReplyVideoMsg
            {
                FromUserName = mchId,
                ToUserName = openId,
                Video = new Video
                {
                    MediaId = mediaId,
                    Title = title,
                    Description = des
                }
            };
            reply = XmlHelper.ToXml(replyMsg);
        }
        /// <summary>
        /// 回复语音消息 上传的素材ID
        /// </summary>
        /// <param name="mediaId"></param>
        protected void ReplyVoiceMsg(string mediaId)
        {
            var replyMsg = new ReplyVoiceMsg
            {
                FromUserName = mchId,
                ToUserName = openId,
                Voice = new Voice
                {
                    MediaId = mediaId
                }
            };
            reply = XmlHelper.ToXml(replyMsg);
        }
        #endregion

        #region 调度器和验签
        /// <summary>
        /// 验证微信服务器的数据
        /// </summary>
        private bool IsLegal
        {
            get
            {
                var signature = context.Request.Query["signature"].ToString().ToLower();
                var timestamp = context.Request.Query["timestamp"].ToString();
                var nonce = context.Request.Query["nonce"].ToString();
                string[] array = { wxApi.apiOption.token, timestamp, nonce };
                Array.Sort(array);
                var arrayString = string.Join("", array);
                var str = EncyptHelper.ToSha1(arrayString).ToLower();
                if (string.Compare(str, signature, true) == 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        /// <summary>
        /// 消息调度器
        /// </summary>
        /// <param name="type"></param>
        /// <param name="postStr"></param>
        /// <returns></returns>
        private void MsgDispatcher(MsgType type, string postStr)
        {
            switch (type)
            {
                case MsgType.text:
                    {
                        var msg = XmlHelper.ToObject<TextMsg>(postStr);
                        TextMsg(msg);
                        break;
                    }
                case MsgType.image:
                    {
                        var msg = XmlHelper.ToObject<ImageMsg>(postStr);
                        ImageMsg(msg);
                        break;
                    }
                case MsgType.voice:
                    {
                        var msg = XmlHelper.ToObject<VoiceMsg>(postStr);
                        VoiceMsg(msg);
                        break;
                    }
                case MsgType.video:
                    {
                        var msg = XmlHelper.ToObject<VideoMsg>(postStr);
                        VideoMsg(msg);
                        break;
                    }
                case MsgType.location:
                    {
                        var msg = XmlHelper.ToObject<LocationMsg>(postStr);
                        LocationMsg(msg);
                        break;
                    }
                case MsgType.link:
                    {
                        var msg = XmlHelper.ToObject<LinkMsg>(postStr);
                        LinkMsg(msg);
                        break;
                    }
                case MsgType.@event:
                    {
                        var eventMsg = XmlHelper.ToObject<EventBase>(postStr);
                        var eventType = (EventType)Enum.Parse(typeof(EventType), eventMsg.Event);
                        EventDispatcher(eventType, postStr);
                        break;
                    }
                default:
                    break;
            }
        }
        /// <summary>
        /// 事件调度器
        /// </summary>
        /// <param name="type"></param>
        /// <param name="postStr"></param>
        private void EventDispatcher(EventType type, string postStr)
        {
            switch (type)
            {
                case EventType.CLICK:
                    {
                        var msg = XmlHelper.ToObject<MenuEvent>(postStr);
                        MenuClickEvent(msg);
                        break;
                    }
                case EventType.VIEW:
                    {
                        var msg = XmlHelper.ToObject<MenuEvent>(postStr);
                        MenuViewEvent(msg);
                        break;
                    }
                case EventType.LOCATION:
                    {
                        var msg = XmlHelper.ToObject<LocationEvent>(postStr);
                        LocationEvent(msg);
                        break;
                    }
                case EventType.SCAN:
                    {
                        var msg = XmlHelper.ToObject<ScanQRCodeEvent>(postStr);
                        ScanQRCodeEvent(msg);
                        break;
                    }
                case EventType.unsubscribe:
                    {
                        var msg = XmlHelper.ToObject<UnSubscribeEvent>(postStr);
                        UnSubscribeEvent(msg);
                        break;
                    }
                case EventType.subscribe:
                    {
                        var msg = XmlHelper.ToObject<ScanQRCodeEvent>(postStr);
                        if (msg.EventKey.Contains("qrscene_"))
                        {
                            msg.EventKey = msg.EventKey.Replace("qrscene_", "");
                            ScanQRCodeEvent(msg);
                            break;
                        }
                        var sub = XmlHelper.ToObject<SubscribeEvent>(postStr);
                        SubscribeEvent(sub);
                        break;
                    }
                case EventType.TEMPLATESENDJOBFINISH:
                    {
                        var msg = XmlHelper.ToObject<TemplateMsgEvent>(postStr);
                        TemplateMsgEvent(msg);
                        break;
                    }
                default:
                    break;
            }
        }
        #endregion
        async Task<string> IWxMessage.HaveMsg()
        {
            if (!IsLegal)
            {
                return "验签不通过";
            }
            if (context.Request.Method.ToUpper() == "POST")
            {
                using (Stream stream = context.Request.Body)
                {
                    byte[] bytes = new byte[context.Request.ContentLength.Value];
                    stream.Read(bytes, 0, bytes.Length);
                    stream.Dispose();
                    string postString = Encoding.UTF8.GetString(bytes);
                    if (!string.IsNullOrEmpty(postString))
                    {
                        var msg = XmlHelper.ToObject<Msg>(postString);
                        openId = msg.FromUserName;
                        mchId = msg.ToUserName;
                        return await Task<string>.Run(() => {
                            var msgType = (MsgType)Enum.Parse(typeof(MsgType), msg.MsgType);
                            MsgDispatcher(msgType, postString);
                            return reply;
                        });
                    }
                }
            }
            Encoding.GetEncoding("utf-8");
            string echoString = context.Request.Query["echostr"].ToString();
            return echoString;
        }
    }
}
